import logging

from carts.utils import merge_cart_cookie_to_redis
from goods.models import SKU
from users.utils import generate_verify_email_url

logger = logging.getLogger('django')

from django import http
from django.contrib.auth import login, authenticate, logout

# from django.contrib.auth.models import User

from users.models import User, Address
import re

from django.http import JsonResponse
from django.views import View
import json
from django_redis import get_redis_connection
from celery_tasks.email.tasks import send_verify_email


# Create your views here.
# 判断用户名重复注册接口
class UsernameCountView(View):

    # 由于接口定义的请求方式是GET，所以映射的视图方法是self.get()
    # 所以我们需要定义self.get()视图方法
    def get(self, request, username):
        # 1、提取参数
        # username是路径参数，在路由匹配的时候通过正则或者转化器提取传递到视图函数中。
        # 2、校验参数
        # 路径参数正则匹配或转化器匹配，已经起到了校验的作用。
        # 3、数据处理 —— 根据username统计用户数量
        try:
            count = User.objects.filter(
                username=username
            ).count()
        except Exception as e:
            # 记录日志
            logger.info("数据库访问失败！", e)
            # 构建错误响应
            return JsonResponse({
                "code": 400,
                "errmsg": "数据库错误"
            })

        # 4、构建响应
        return JsonResponse({
            "code": 0,
            "errmsg": "ok",
            "count": count
        })


# 判断手机号重复注册
class MobileCountView(View):

    def get(self, request, mobile):
        # 1、提取参数
        # 2、校验参数
        # 3、业务数据处理 —— 根据手机号统计注册用户数量
        try:
            count = User.objects.filter(
                mobile=mobile
            ).count()
        except Exception as e:
            # 记录日志
            logger.info("数据库访问失败！")
            # 构建错误响应
            return JsonResponse({
                "code": 400,
                "errmsg": "数据库错误"
            })
        # 4、构建响应
        return JsonResponse({
            'code': 0,
            'errmsg': 'ok',
            'count': count
        })


# 用户注册
class RegisterView(View):

    def post(self, request):
        """接收参数，保存到数据库"""
        json_bytes = request.body  # 从请求体中获取原石的JSON数据，bytes类型
        json_str = json_bytes.decode()
        json_dict = json.loads(json_str)

        # 提取参数
        username = json_dict.get('username')
        password = json_dict.get('password')
        password2 = json_dict.get('password2')
        mobile = json_dict.get('mobile')
        sms_code = json_dict.get('sms_code')
        allow = json_dict.get('allow')

        # 校验是否为空
        if not all([username, password, password2, mobile, sms_code, allow]):
            return http.JsonResponse({
                'code': 400,
                'errmsg': '缺少必传参数'
            }, status=400)

        # 校验用户名
        if not re.match(r'^[a-zA-Z0-9_-]{5,20}$', username):
            return http.JsonResponse({
                'code': 400,
                'errmsg': 'username格式错误'
            }, status=400)

        # 校验password
        if not re.match(r'^[a-zA-Z0-9_-]{8,20}$', password):
            return http.JsonResponse({
                'code': 400,
                'errmsg': 'password格式错误'
            }, status=400)

        # 校验password 和 password2
        if password != password2:
            return http.JsonResponse({
                'code': 400,
                'errmsg': '两次输入不一致'
            }, status=400)

        # mobile校验
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return JsonResponse({
                'code': 400,
                'errmsg': '手机号格式有误'
            }, status=400)

        # TODO: 短信校验
        redis_conn = get_redis_connection('verify_code')
        sms_code_server = redis_conn.get('sms_%s' % mobile)

        # 判断短信验证码是否过期
        if not sms_code_server:
            return http.JsonResponse({
                'code': 400,
                'errmsg': '短信验证码失效'
            }, status=400)
        if sms_code != sms_code_server.decode():
            return http.JsonResponse({
                'code': 400,
                'errmsg': '短信验证码输入有误'
            }, status=400)

        # allow校验
        if allow != True:
            return http.JsonResponse({
                'code': 400,
                'errmsg': 'allow格式错误'
            }, status=400)

        try:
            # 密码加密
            # 实例化MD5加密
            # md5 = hashlib.md5()
            # md5.update(password.encode())
            # pwd = md5.hexdigest()

            user = User.objects.create_user(username=username, password=password, mobile=mobile)
        except Exception as e:
            return http.JsonResponse({
                'code': 400,
                'errmsg': '注册失败！'
            })
        # TODO：用户状态保持，session
        login(request, user)

        # 生成响应对象
        response = http.JsonResponse({'code': 0, 'errmsg': '注册成功'})

        # 在响应对象中设置用户名信息.
        # 将用户名写入到 cookie，有效期 14 天
        response.set_cookie('username', user.username, 3600 * 24 * 14)

        # 返回响应结果
        return response


#  登录
class LoginView(View):
    def post(self, request):
        """接收参数，保存到数据库"""
        json_dict = json.loads(request.body.decode())
        # 提取参数
        username = json_dict.get('username')
        password = json_dict.get('password')
        remembered = json_dict.get('remembered')

        # 校验是否为空
        if not all([username, password]):
            return http.JsonResponse({
                'code': 400,
                'errmsg': '缺少必传参数'
            })

        # 实现多账号登录
        # 判断用户输入的账号是用户名还是手机号
        # USERNAME_FIELD 修改内置配置文件参数  = 'mobile'
        # if re.match(r'^1[3-9]\d{9}$', username):
        #     # 用户输入的账号是手机号:将USERNAME_FIELD指定为'mobile'字段
        #     User.USERNAME_FIELD = 'mobile'
        # else:
        #     # 用户输入的账号是用户名:将USERNAME_FIELD指定为'username'字段
        #     User.USERNAME_FIELD = 'username'
        # 验证是否能够登陆
        user = authenticate(request, username=username, password=password)
        # 判断是否为空,如果为空,返回
        if user is None:
            return http.JsonResponse({
                'code': 400,
                'errmsg': '用户名或者密码错误'
            })

        login(request, user)
        # print(request.user)

        # 判断是否记住登陆用户
        if remembered != True:
            request.session.set_expiry(0)
        else:
            request.session.set_expiry(None)

        # 生成响应对象
        response = http.JsonResponse({'code': 0, 'errmsg': 'ok'})
        # 在响应对象中设置用户名信息.
        # 将用户名写入到 cookie，有效期 14 天
        response.set_cookie('username', user.username, 3600 * 24 * 14)

        # TODO:合并购物车
        merge_cart_cookie_to_redis(request=request, response=response)

        # 返回响应结果
        return response


# 退出登录
class LogoutView(View):
    '''定义退出登录的接口'''

    def delete(self, request):
        # 清理session
        logout(request)

        # 创建response对象
        response = http.JsonResponse({
            'code': 0,
            'errmsg': 'OK'
        })

        # 调用对象的 delete_cookie方法，清除cookie
        response.delete_cookie('username')

        # 返回响应
        return response


# 用户中心
from meiduo_mall.utils.views import LoginRequiredJSONMixin


class UserInfoView(LoginRequiredJSONMixin, View):
    """用户中心"""

    def get(self, request):
        """提供个人信息界面"""

        # 获取界面需要的数据,进行拼接
        info_data = {
            "code": 0,
            "errmsg": "ok",
            "info_data": {
                "username": request.user.username,
                "mobile": request.user.mobile,
                "email": request.user.email,
                "email_active": request.user.email_active
            }
        }
        # 返回响应
        return http.JsonResponse(info_data)


# 邮箱验证
class EmailView(LoginRequiredJSONMixin, View):
    def put(self, request):
        json_dict = json.loads(request.body.decode('utf-8'))
        email = json_dict.get('email')

        # 校验是否输入email
        if not email:
            return JsonResponse({
                'code': 400,
                'errmsg': '缺少email参数'
            }, status=400)

        # 校验email格式
        if not re.match(r'^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$', email):
            return JsonResponse({
                'code': 400,
                'errmsg': 'email格式错误'
            })

        # 将email写入数据库表
        try:
            request.user.email = email
            request.user.save()
        except Exception as e:
            logger.error(f'数据库更新失败：{e}')
            return JsonResponse({
                'code': 400,
                'errmsg': '添加email失败'
            }, status=400)

        # TODO:补全邮箱验证
        # 调用发送邮件的函数
        verify_url = generate_verify_email_url(request)
        # 发送验证链接:
        send_verify_email.delay(email, verify_url)
        print(verify_url)

        return JsonResponse({
            'code': 0,
            'errmsg': '添加邮箱成功'
        })


# TODO:验证邮箱后端逻辑
from meiduo_mall.utils.secret import SecretOauth


class VerifyEmailView(View):
    def put(self, request):
        # - 1.接收 token
        token = request.GET.get('token')
        print(token)

        if not token:
            return JsonResponse({'code': 400, 'errmsg': 'token缺少'})

        # - 2.解密
        data_dict = SecretOauth().loads(token)
        if data_dict is None:
            return JsonResponse({
                'code': 400,
                'errmsg': '邮箱验证失败'
            })
        user_id = data_dict.get('user_id')
        email = data_dict.get('email')

        # - 4.去数据库对比 user_id,email
        try:
            user = User.objects.get(pk=user_id, email=email, is_active=True)
        except User.DoesNotExist as e:
            logger.info(f'邮箱验证用户不存在或已注销：{e}')
            return JsonResponse({'code': 400, 'errmsg': '邮箱验证用户不存在或已注销!'})
        else:
            # - 5.修改激活状态
            user.email_active = True
            user.save()

            return JsonResponse({'code': 0, 'errmsg': 'ok'})


from .models import Address


class CreateAddressView(View):
    def post(self, request):
        # 提取参数
        json_dict = json.loads(request.body.decode())

        receiver = json_dict.get('receiver')
        province_id = json_dict.get('province_id')
        city_id = json_dict.get('city_id')
        district_id = json_dict.get('district_id')
        place = json_dict.get('place')
        mobile = json_dict.get('mobile')
        tel = json_dict.get('tel')
        email = json_dict.get('email')

        user = request.user
        count = user.addresses.filter(is_deleted=False).count()
        if count >= 20:
            return JsonResponse({
                'code': 400,
                'errmsg': '最多20个地址'
            })

        if not all([
            receiver,
            province_id,
            city_id,
            district_id,
            place,
            mobile
        ]):
            return http.JsonResponse({
                'code': 400,
                'errmsg': '缺少参数'
            })

        if not re.match(r'^\w{1,20}$', receiver):
            return http.JsonResponse({
                'code': 400,
                'errmsg': '收货人昵称有误'
            })
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return http.JsonResponse({
                'code': 400,
                'errmsg': '手机号格式有误'
            })
        if tel:
            if not re.match(r'^(\d{4}-)?\d{6,8}$', tel):
                return JsonResponse({
                    'code': 400,
                    'errmsg': '参数tel格式有误'
                })
        if email:
            if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
                return JsonResponse({
                    'code': 400,
                    'errmsg': '参数email格式有误'
                })

        # 业务处理 -- 新建地址对象保存到数据库
        try:
            address = Address.objects.create(
                user=request.user,
                province_id=province_id,
                city_id=city_id,
                district_id=district_id,
                title=receiver,  # 新增地址没有传递标题，默认把标题设置为receiver
                receiver=receiver,
                place=place,
                mobile=mobile,
                tel=tel or '',
                email=email or ''
            )
            # 如果用户的default_address为空，把新增的收货地址设置为用户的默认地址
            user = request.user
            if not user.default_address:
                user.default_address = address
                user.save()
        except Exception as e:
            return JsonResponse({
                'code': 400,
                'errmsg': '保存收货地址错误'
            })

        # 构建响应
        return JsonResponse({
            'code': 0,
            'errmsg': 'ok',
            'address': {
                'id': address.id,
                'title': address.title,
                'receiver': address.receiver,
                'province': address.province.name,
                'city': address.city.name,
                'district': address.district.name,
                'place': address.place,
                'mobile': address.mobile,
                'tel': address.tel,
                'email': address.email
            }
        })


# 展示收货地址
class AddressView(View):
    def get(self, request):
        addresses = Address.objects.filter(is_deleted=False)
        # 创建空列表存放数据
        address_list = []
        for address in addresses:
            address_dict = {
                'id': address.id,
                'title': address.title,
                'receiver': address.receiver,
                'province': address.province.name,
                'city': address.city.name,
                'district': address.district.name,
                'place': address.place,
                'mobile': address.mobile,
                'tel': address.tel,
                'email': address.email
            }
            if address.id == request.user.default_address_id:
                address_list.insert(0, address_dict)
            else:
                address_list.append(address_dict)

        default_id = request.user.default_address_id
        # 构建响应
        return http.JsonResponse({
            'code': 0,
            'errmsg': 'ok',
            'default_address_id': default_id,
            'addresses': address_list
        })


# 修改收货地址
class UpdateDestroyAddressView(View):
    '''修改或删除（逻辑删除）地址信息'''

    def put(self, request, address_id):
        json_dict = json.loads(request.body.decode())

        receiver = json_dict.get('receiver')
        province_id = json_dict.get('province_id')
        city_id = json_dict.get('city_id')
        district_id = json_dict.get('district_id')
        place = json_dict.get('place')
        mobile = json_dict.get('mobile')
        tel = json_dict.get('tel')
        email = json_dict.get('email')

        if not all([
            receiver, province_id, city_id, district_id, place, mobile
        ]):
            return JsonResponse({
                'code': 400,
                'errmsg': '缺少参数'
            })

        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return http.JsonResponse({
                'code': 400,
                'errmsg': '手机号格式有误'
            })

        if tel:
            if not re.match(r'^(\d{4}-)?\d{6,8}$', tel):
                return JsonResponse({
                    'code': 400,
                    'errmsg': '参数tel格式有误'
                })
        if email:
            if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
                return JsonResponse({
                    'code': 400,
                    'errmsg': '参数email格式有误'
                })

        # 修改保存数据
        try:
            # Address.objects.filter(id=address_id).update(
            #     user=request.user,
            #     province_id=province_id,
            #     city_id=city_id,
            #     district_id=district_id,
            #     title=receiver,  # 如果地址没有传递标题，默认把标题设置为receiver
            #     receiver=receiver,
            #     place=place,
            #     mobile=mobile,
            #     tel=tel,
            #     email=email
            # )

            # update(**json_dict) 函数解包传参 -- 把字典的键值对，解包为关键字参数传入参数
            json_dict.pop('province')
            json_dict.pop('city')
            json_dict.pop('district')
            Address.objects.filter(id=address_id).update(**json_dict)

        except Exception as e:
            logger.info('数据异常：', e)
            return JsonResponse({
                'code': 400,
                'errmsg': '数据更新错误'
            })

        # 构造响应数据
        address = Address.objects.get(id=address_id)

        # 响应更新地址结果
        return http.JsonResponse({
            'code': 0,
            'errmsg': '更新地址成功',
            'address': {
                "id": address.id,
                "title": address.title,
                "receiver": address.receiver,
                "province": address.province.name,
                "city": address.city.name,
                "district": address.district.name,
                "place": address.place,
                "mobile": address.mobile,
                "tel": address.tel,
                "email": address.email
            }
        })

    def delete(self, request, address_id):
        try:
            address = Address.objects.get(id=address_id, is_deleted=False)
            address.is_deleted = True
            address.save()

            user = request.user
            if address.id == user.default_address.id:
                # 如果当前删除的地址刚好是用户的默认地址，把默认地址设置为用户最新增加的地址
                addresses = Address.objects.filter(user=user, is_deleted=False).order_by('-update_time')  # 地址查询集 或 一个空集
                if addresses:
                    user.default_address = addresses[0]
                else:
                    user.default_address = None
                user.save()


        except Exception as e:
            logger.info('数据库错误:', e)
            return JsonResponse({
                'code': 400,
                'errmsg': '删除地址失败'
            })

        return JsonResponse({
            'code': 0,
            'errmsg': '删除地址成功'
        })


# 设置默认地址
class DefaultAddressView(View):
    def put(self, request, address_id):
        try:
            address = Address.objects.get(id=address_id)
        except Exception:
            logger.error('数据库查询失败')
            return JsonResponse({
                'code': 400,
                'errmsg': '数据库查询失败'
            })
        try:
            # User.objects.filter(id=request.user.id).update(default_address=address)
            request.user.default_address = address
            request.user.save()
        except Exception:
            logger.error('数据库修改失败')
            return JsonResponse({
                'code': 400,
                'errmsg': '数据库修改失败'
            })
        return JsonResponse({
            'code': 0,
            'errmsg': 'ok'
        })


# 更新地址标题
class UpdateTitleAddressView(View):
    def put(self, request, address_id):
        # 接受title参数
        json_dict = json.loads(request.body.decode())
        title = json_dict.get('title')

        try:
            address = Address.objects.get(id=address_id)

            # 设置新的标题
            address.title = title
            address.save()
        except Exception as e:
            logger.error(e)
            return JsonResponse({
                'code': 400,
                'errmsg': '设置标题失败'
            })
        return JsonResponse({
            'code': 0,
            'errmsg': 'ok'
        })


# 修改密码
class ChangePasswordView(View):
    def put(self, request):
        json_dict = json.loads(request.body.decode())

        old_password = json_dict.get('old_password')
        new_password = json_dict.get('new_password')
        new_password2 = json_dict.get('new_password2')

        if not all([old_password, new_password, new_password2]):
            return JsonResponse({
                'code': 400,
                'errmsg': '缺少参数'
            })

        user = request.user
        if not user.check_password(old_password):
            return JsonResponse({
                'code': 400,
                'errmsg': '原始密码不正确'
            })
        # 校验新密码格式
        if not re.match(r'^[0-9A-Za-z]{8,20}$', new_password):
            return http.JsonResponse({
                'code': 400,
                'errmsg': '密码最少8位,最长20位'
            })

        if new_password != new_password2:
            return http.JsonResponse({
                'code': 400,
                'errmsg': '两次输入密码不一致'
            })

        # 保存密码
        try:
            user.set_password(new_password)
            user.save()
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({
                'code': 400,
                'errmsg': '修改密码失败'
            })

        logout(request)

        response = http.JsonResponse({
            'code': 0,
            'errmsg': 'ok'
        })
        response.delete_cookie('username')

        return response

# 用户浏览历史记录
# id__in ==> select * from 表 where id in (1，2,3,4,5);
class UserBrowseHistory(LoginRequiredJSONMixin,View):
    def post(self,request):
        """保存用户浏览记录"""
        json_dict = json.loads(request.body.decode())
        sku_id = json_dict.get('sku_id')

        # 校验参数
        try:
            SKU.objects.get(id=sku_id)
        except SKU.DoesNotExist:
            return http.HttpResponseForbidden('sku不存在')

        # 保存用户浏览数据
        redis_conn = get_redis_connection('history')
        # 用管道方式执行，提高效率
        pl = redis_conn.pipeline()
        save_key = f'history_{request.user.id}'

        # 1、去重:此处用 0 代表去除所有的 sku_id
        pl.lrem(save_key,0,sku_id)
        # 2、存储
        pl.lpush(save_key,sku_id)
        # 3、截取数据：只保留5个数据
        pl.ltrim(save_key,0,4)
        # 执行管道
        pl.execute()

        # 响应结果
        return http.JsonResponse({
            'code': 0,
            'errmsg': 'ok'
        })

    def get(self,request):
        """获取用户浏览记录"""
        redis_conn = get_redis_connection('history')
        sku_ids = redis_conn.lrange(f'history_{request.user.id}',0,-1)

        # 根据sku_ids列表数据，查询出商品sku信息
        skus = []
        for sku_id in sku_ids:
            sku = SKU.objects.get(id=sku_id)
            skus.append({
                'id': sku.id,
                'name': sku.name,
                'default_image_url': sku.default_image.url,
                'price': sku.price
            })

        return JsonResponse({
            'code': 0,
            'errmsg': 'ok',
            'skus': skus
        })

